/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.dy.yunying.biz.service.manage.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dy.yunying.api.dto.ImportCdkDTO;
import com.dy.yunying.api.entity.GameRole;
import com.dy.yunying.api.entity.ParentGameArea;
import com.dy.yunying.api.entity.ParentGameDO;
import com.dy.yunying.api.entity.WanCdk;
import com.dy.yunying.api.entity.WanUser;
import com.dy.yunying.api.entity.WanGameDO;
import com.dy.yunying.api.entity.hongbao.HbActivity;
import com.dy.yunying.api.entity.prize.PrizePush;
import com.dy.yunying.api.entity.sign.SignActivity;
import com.dy.yunying.api.enums.CdkSourceTypeEnum;
import com.dy.yunying.api.req.hongbao.GenerateCdkReq;
import com.dy.yunying.api.req.hongbao.GrantCdkReq;
import com.dy.yunying.api.req.hongbao.ImportCdkReq;
import com.dy.yunying.api.resp.hongbao.AdminUserVo;
import com.dy.yunying.api.resp.hongbao.CdkVo;
import com.dy.yunying.api.resp.hongbao.OptionData;
import com.dy.yunying.api.resp.hongbao.WanUserVo;
import com.dy.yunying.api.utils.RandomUtils;
import com.dy.yunying.biz.dao.ads.hongbao.HbActivityMapper;
import com.dy.yunying.biz.dao.ads.sign.SignActivityMapper;
import com.dy.yunying.biz.dao.manage.GameRoleMapper;
import com.dy.yunying.biz.dao.manage.ParentGameAreaMapper;
import com.dy.yunying.biz.dao.manage.WanCdkMapper;
import com.dy.yunying.biz.dao.manage.WanUserMapper;
import com.dy.yunying.biz.dao.manage.WanGameDOMapper;
import com.dy.yunying.biz.dao.manage.ext.ParentGameDOMapperExt;
import com.dy.yunying.biz.service.manage.WanCdkService;
import com.dy.yunying.biz.service.prize.PrizePushService;
import com.dy.yunying.biz.utils.DateUtils;
import com.pig4cloud.pig.admin.api.dto.UserInfo;
import com.pig4cloud.pig.admin.api.entity.SysUser;
import com.pig4cloud.pig.admin.api.feign.RemoteUserService;
import com.pig4cloud.pig.common.core.constant.CommonConstants;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * CDK信息表
 *
 * @author yuwenfeng
 * @date 2021-11-16 09:57:41
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class WanCdkServiceImpl extends ServiceImpl<WanCdkMapper, WanCdk> implements WanCdkService {

	private final HbActivityMapper activityMapper;

	private final RemoteUserService remoteUserService;

	private final ParentGameDOMapperExt parentGameDOMapperExt;

	private final WanUserMapper wanUserMapper;

	private final WanGameDOMapper wanGameDOMapper;

	private final SignActivityMapper signActivityMapper;

	private final GameRoleMapper gameRoleMapper;

	private final ParentGameAreaMapper parentGameAreaMapper;

	private final PrizePushService prizePushService;

	@Override
	public List<AdminUserVo> selectUserList() {
		return activityMapper.selectAdminUserList(SecurityUtils.getTenantId());
	}

	@Override
	public Page<CdkVo> selectCdkPage(Page<WanCdk> page, QueryWrapper<WanCdk> query) {
		Page<CdkVo> result = baseMapper.selectCdkPage(page, query);
		if (CollectionUtils.isNotEmpty(result.getRecords())) {
			List<Integer> giveIds = result.getRecords().stream().map(m -> null != m.getGiveId() ? m.getGiveId().intValue() : null).distinct().collect(Collectors.toList());
			if (CollectionUtils.isNotEmpty(giveIds)) {
				R<List<SysUser>> userInfoData = remoteUserService.getUserListByUserIds(SecurityConstants.FROM_IN, giveIds);
				for (CdkVo vo : result.getRecords()) {
					if (null != userInfoData && CommonConstants.SUCCESS.equals(userInfoData.getCode())) {
						if (CollectionUtils.isNotEmpty(userInfoData.getData())) {
							for (SysUser sysUser : userInfoData.getData()) {
								if (null != vo.getGiveId() && vo.getGiveId() > 0) {
									if (vo.getGiveId().intValue() == sysUser.getUserId()) {
										vo.setGiveName(sysUser.getRealName());
									}
								}
							}
						}
					}
				}
			}
		}
		return result;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public R generateCdk(GenerateCdkReq req) {
		if (req.getLimitMoney().doubleValue() <= req.getMoney().doubleValue()) {
			return R.failed("满可用金额必须大于代金券金额");
		}
		Date nowTime = new Date();
		WanCdk info = new WanCdk();
		info.setCreateTime(nowTime);
		info.setCdkName(req.getCdkName());
		info.setCreateUserId(SecurityUtils.getUser().getId().longValue());
		info.setCreateUserName(SecurityUtils.getUser().getUsername());
		R<UserInfo> userInfo = remoteUserService.findUserByUserId(SecurityUtils.getUser().getId(), SecurityConstants.FROM_IN);
		if (null != userInfo.getData()) {
			SysUser sysUser = userInfo.getData().getSysUser();
			if (null != sysUser) {
				info.setCreateUserName(sysUser.getRealName());
			}
		}
		info.setUseStatus(10);
		info.setMoney(req.getMoney());
		info.setLimitMoneyType(2);
		info.setLimitMoney(req.getLimitMoney());
		if (StringUtils.isNotBlank(req.getRemark())) {
			info.setRemark(req.getRemark());
		}
		if (null != req.getPgid() && req.getPgid() > 0) {
			info.setPgid(req.getPgid());
		}
		if (null != req.getGameid() && req.getGameid() > 0) {
			info.setGameid(req.getGameid());
		}
		info.setCdkLimitType(1);
		if (null != info.getPgid() || null != info.getGameid()) {
			info.setCdkLimitType(2);
		}
		info.setCdkType(4);
		if (2 == req.getExpiryType()) {
			info.setExpiryType(req.getExpiryType());
			if (1 == req.getValidType()) {
				if (StringUtils.isBlank(req.getStartTime())) {
					return R.failed("有效开始时间不能为空");
				}
				Date startTime = DateUtils.stringToDate(req.getStartTime(), DateUtils.YYYY_MM_DD_HH_MM_SS);
				if (null == startTime) {
					return R.failed("有效开始时间不合法");
				}
				if (StringUtils.isBlank(req.getEndTime())) {
					return R.failed("有效到期时间不能为空");
				}
				Date endTime = DateUtils.stringToDate(req.getEndTime(), DateUtils.YYYY_MM_DD_HH_MM_SS);
				if (null == endTime) {
					return R.failed("有效到期时间不合法");
				}
				if (endTime.getTime() <= startTime.getTime()) {
					return R.failed("结束时间不能比开始时间小");
				}
				info.setStartTime(startTime);
				info.setEndTime(endTime);
			} else if (2 == req.getValidType()) {
				if (null == req.getValidDays() || req.getValidDays() <= 0 || req.getValidDays() > 365) {
					return R.failed("有效天数不合法");
				}
				info.setStartTime(nowTime);
				info.setEndTime(DateUtils.addDays(nowTime, req.getValidDays()));
			} else {
				return R.failed("限制类型无法识别");
			}
		} else {
			info.setExpiryType(1);
		}
		//获取所有代金券记录
		List<WanCdk> dataList = baseMapper.selectList(Wrappers.<WanCdk>lambdaQuery().ge(WanCdk::getCreateTime, DateUtils.dateToString(DateUtils.addMinute(new Date(), -10), DateUtils.YYYY_MM_DD_HH_MM_SS)));
		List<String> codeList = dataList.stream().map(WanCdk::getCdkCode).collect(Collectors.toList());
		List<WanCdk> list = new ArrayList<WanCdk>();
		Set<String> codeSet = new HashSet<String>();
		codeSet = generateSetCode(codeSet, req.getNum(), codeList);
		for (String code : codeSet) {
			System.out.println(code);
			WanCdk data = JSON.parseObject(JSON.toJSONString(info), WanCdk.class);
			data.setCdkCode(code);
			list.add(data);
		}
		this.saveBatch(list);
		return R.ok();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public R grantCdk(GrantCdkReq req) {
		if (null == req.getGrantType()) {
			return R.failed("发放类型不能为空");
		}
		WanCdk cdk = baseMapper.selectOne(new QueryWrapper<WanCdk>().eq("cdk_code", req.getCdkCode()).eq("use_status", 10));
		if (null == cdk) {
			return R.failed("该代金券不存在或已被使用");
		}
		if (null != cdk.getBelongsId() && cdk.getBelongsId() > 0) {
			return R.failed("该代金券属于其他会员");
		}
		if (1 == req.getGrantType()) {
			if (StringUtils.isBlank(req.getBelongsName())) {
				return R.failed("账号不能为空");
			}
			if (req.getBelongsName().length() < 1 || req.getBelongsName().length() > 50) {
				return R.failed("账号字符长度不合法");
			}
			//从mongo获取
			WanUserVo wanUser = baseMapper.selectUserByName(req.getBelongsName().trim());
			if (null == wanUser) {
				return R.failed("该账号未能找到用户");
			}
			cdk.setBelongsId(wanUser.getUserId());
			if (StringUtils.isBlank(cdk.getRemark())) {
				StringBuilder remarkSb = new StringBuilder();
				if (1 == cdk.getCdkLimitType()) {
					remarkSb.append("无门槛券");
				} else if (2 == cdk.getCdkLimitType()) {
					remarkSb.append("仅限：");
					remarkSb.append("《");
					ParentGameDO parentGame = null;
					if (null != cdk.getPgid()) {
						parentGame = parentGameDOMapperExt.selectByPrimaryKey(cdk.getPgid());
					}
					WanGameDO wanGame = null;
					if (null != cdk.getGameid()) {
						wanGame = wanGameDOMapper.selectByPrimaryKey(cdk.getGameid());
					}
					if (null != wanGame) {
						if (StringUtils.isNotBlank(wanGame.getAppName())) {
							remarkSb.append(wanGame.getAppName());
						} else {
							if (null == parentGame) {
								parentGame = parentGameDOMapperExt.selectByPrimaryKey(wanGame.getPgid());
							}
							if (null != parentGame) {
								remarkSb.append(parentGame.getGname());
							}
						}
					} else {
						if (null != parentGame) {
							remarkSb.append(parentGame.getGname());
						}
					}
					remarkSb.append("》使用");
				}
				cdk.setRemark(remarkSb.toString());
			}
		} else if (2 == req.getGrantType()) {
			if (null == req.getPgid() || req.getPgid() <= 0) {
				return R.failed("父游戏不能为空");
			}
			if (null == req.getAreaId() || req.getAreaId() <= 0) {
				return R.failed("区服不能为空");
			}
			if (StringUtils.isBlank(req.getRoleId())) {
				return R.failed("角色不能为空");
			}
			GameRole gameRole = gameRoleMapper.selectOne(new QueryWrapper<GameRole>().eq("pgame_id", req.getPgid()).eq("area_id", req.getAreaId()).eq("role_id", req.getRoleId().trim()).last("LIMIT 1"));
			if (null == gameRole) {
				return R.failed("该区服不存在该角色");
			}
			cdk.setGameid(gameRole.getGameId());
			cdk.setPgid(gameRole.getPgameId());
			cdk.setAreaId(gameRole.getAreaId().toString());
			cdk.setRoleId(gameRole.getRoleId());
			cdk.setBelongsId(gameRole.getUserId());
			cdk.setCdkLimitType(3);
			cdk.setRoleName(gameRole.getRoleName());
			if (StringUtils.isBlank(cdk.getRemark())) {
				StringBuilder remarkSb = new StringBuilder();
				remarkSb.append("仅限：");
				List<ParentGameArea> areaList = parentGameAreaMapper.selectList(new QueryWrapper<ParentGameArea>());
				if (null != cdk.getGameid()) {
					WanGameDO wanGame = wanGameDOMapper.selectByPrimaryKey(cdk.getGameid());
					ParentGameDO parentGame;
					if (null == wanGame) {
						parentGame = parentGameDOMapperExt.selectByPrimaryKey(cdk.getPgid());
						if (null != parentGame) {
							remarkSb.append(parentGame.getGname() + "-");
						}
					} else {
						if (StringUtils.isNotBlank(wanGame.getAppName())) {
							remarkSb.append(wanGame.getAppName() + "-");
						} else {
							parentGame = parentGameDOMapperExt.selectByPrimaryKey(cdk.getPgid());
							if (null != parentGame) {
								remarkSb.append(parentGame.getGname() + "-");
							}
						}
					}
				}
				String areaName = getAreaById(cdk.getPgid(), cdk.getAreaId(), areaList);
				if (StringUtils.isNotBlank(areaName)) {
					remarkSb.append(areaName + "-");
				}
				remarkSb.append(gameRole.getRoleName() + "使用");
				cdk.setRemark(remarkSb.toString());
			}
		} else {
			return R.failed("暂不支持该发放类型");
		}
		cdk.setGiveTime(new Date());
		cdk.setGiveId(SecurityUtils.getUser().getId().longValue());
		baseMapper.updateById(cdk);
		return R.ok();
	}

	/**
	 * 导入代金券数据
	 *
	 * @param req
	 */
	@Transactional(transactionManager = "main3399TransactionManager", rollbackFor = Exception.class)
	@Override
	public void importCdk(ImportCdkReq req) throws IOException {
		final MultipartFile cdkExcelFile = req.getCdkExcelFile();
		if (null == cdkExcelFile || cdkExcelFile.isEmpty()) {
			throw new IllegalArgumentException("请提供正确的代金券导入文件");
		}
		final int batchCount = 100;
		final Date currentTime = new Date();
		final List<String> cdkCodeList = new LinkedList<>();
		final List<ImportCdkDTO> cdkList = new LinkedList<>();
		EasyExcel.read(cdkExcelFile.getInputStream(), ImportCdkDTO.class, new AnalysisEventListener<ImportCdkDTO>() {
			@Override
			public void invoke(ImportCdkDTO cdk, AnalysisContext context) {
				if (null == cdk) {
					return;
				}
				cdkList.add(cdk);
				if (cdkList.size() >= batchCount) {
					WanCdkServiceImpl.this.batchGrantCdkList(cdkList, cdkCodeList, currentTime);
					cdkList.clear();
				}
			}

			@Override
			public void doAfterAllAnalysed(AnalysisContext context) {
				if (cdkList.size() > 0) {
					WanCdkServiceImpl.this.batchGrantCdkList(cdkList, cdkCodeList, currentTime);
					cdkList.clear();
				}
			}
		}).sheet().doRead();
	}

	@Transactional(transactionManager = "main3399TransactionManager", rollbackFor = Exception.class)
	public void batchGrantCdkList(List<ImportCdkDTO> list, List<String> cdkCodeList, Date currentTime) {
		final List<String> usernameList = list.stream().map(ImportCdkDTO::getUsername).distinct().collect(Collectors.toList());
		final Map<String, Long> userMap = wanUserMapper.selectList(Wrappers.<WanUser>lambdaQuery().select(WanUser::getUserid, WanUser::getUsername).in(WanUser::getUsername, usernameList)).stream().collect(Collectors.toMap(WanUser::getUsername, WanUser::getUserid, (k1, k2) -> k1));

		final List<WanCdk> saveList = new LinkedList<>();

		for (ImportCdkDTO cdk : list) {
			final Long userId = userMap.get(cdk.getUsername());
			if (null == userId) {
				throw new NullPointerException("用户名: " + cdk.getUsername() + " 不存在");
			}
			if (cdk.getCdkAmount().compareTo(cdk.getCdkLimitAmount()) >= 0) {
				throw new IllegalStateException("代金券金额必须小于限制金额");
			}
			final WanCdk wanCdk = new WanCdk();
			wanCdk.setCdkCode(generateCdkCode(cdkCodeList));
			wanCdk.setCdkName(cdk.getCdkName());
			wanCdk.setMoney(cdk.getCdkAmount());
			wanCdk.setBelongsId(userId);
			wanCdk.setCreateTime(currentTime);
			wanCdk.setGiveTime(currentTime);
			wanCdk.setCreateUserId(SecurityUtils.getUser().getId().longValue());
			wanCdk.setCreateUserName(SecurityUtils.getUser().getUsername());
			wanCdk.setExpiryType(2);
			wanCdk.setStartTime(cdk.getStartTime());
			wanCdk.setEndTime(cdk.getEndTime());
			wanCdk.setRemark(cdk.getRemark());
			wanCdk.setUseStatus(10);
			wanCdk.setCdkType(4);
			wanCdk.setLimitMoneyType(2);
			wanCdk.setLimitMoney(cdk.getCdkLimitAmount());
			wanCdk.setPgid(cdk.getPgid());
			saveList.add(wanCdk);
		}
		this.saveBatch(saveList);
	}

	@Override
	public List<OptionData> selectActityListByCdkSourceType(Integer cdkSourceType) {
		List<OptionData> list = new ArrayList<OptionData>();
		if (CdkSourceTypeEnum.LEVEL.getType().equals(cdkSourceType) || CdkSourceTypeEnum.RECHARGE.getType().equals(cdkSourceType) || CdkSourceTypeEnum.INVITATION.getType().equals(cdkSourceType) || CdkSourceTypeEnum.CUSTOM.getType().equals(cdkSourceType)) {
			List<HbActivity> activityList = activityMapper.selectList(new QueryWrapper<HbActivity>().eq("activity_type", cdkSourceType).orderByDesc("id"));
			if (CollectionUtils.isNotEmpty(activityList)) {
				list = activityList.stream().map(activity -> {
					OptionData od = new OptionData();
					od.setId(activity.getId());
					od.setName(activity.getActivityName());
					return od;
				}).collect(Collectors.toList());
			}
		} else if (CdkSourceTypeEnum.SIGNACTIVETY.getType().equals(cdkSourceType)) {
			List<SignActivity> activityList = signActivityMapper.selectList(new QueryWrapper<SignActivity>().orderByDesc("id"));
			if (CollectionUtils.isNotEmpty(activityList)) {
				list = activityList.stream().map(activity -> {
					OptionData od = new OptionData();
					od.setId(activity.getId());
					od.setName(activity.getActivityName());
					return od;
				}).collect(Collectors.toList());
			}
		} else if (CdkSourceTypeEnum.PRIZE.getType().equals(cdkSourceType)){
			List<PrizePush> prizePushList = prizePushService.list(Wrappers.<PrizePush>lambdaQuery().eq(PrizePush::getDeleted, 0)
					.orderByDesc(PrizePush::getId));
			if (CollectionUtils.isNotEmpty(prizePushList)) {
				list = prizePushList.stream().map(prizePush -> {
					OptionData od = new OptionData();
					od.setId(prizePush.getId());
					// 和产品确认奖励下拉列表不显示标题，只显示ID
					od.setName(String.valueOf(prizePush.getId()));
					return od;
				}).collect(Collectors.toList());
			}
		}
		return list;
	}

	/**
	 * @description: 判断代金券码是否重复
	 * @author yuwenfeng
	 * @date 2021/11/16 14:38
	 */
	String generateCdkCode(List<String> codeList) {
		String cdkCode = RandomUtils.getCdkNum();
		if (codeList.contains(cdkCode)) {
			return generateCdkCode(codeList);
		}
		codeList.add(cdkCode);
		return cdkCode;
	}

	Set<String> generateSetCode(Set<String> set, int num, List<String> codeList) {
		while (set.size() < num) {
			set.add(generateCdkCode(codeList));
		}
		return set;
	}

	/**
	 * @description: 根据父游戏、区服ID，获取区服名称
	 * @author yuwenfeng
	 * @date 2022/3/17 20:30
	 */
	private String getAreaById(Long parentGameId, String areaId, List<ParentGameArea> areaList) {
		if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(areaList)) {
			for (ParentGameArea area : areaList) {
				if (parentGameId.equals(area.getParentGameId()) && areaId.equals(area.getAreaId().toString())) {
					return area.getAreaName();
				}
			}
		}
		return "";
	}

}
